5

一、组件

(一)什么是组件

组件(Component)是 Vue.js最强大的功能之一。组件可以扩展 HTML元素,封装可重用的代码组件是自定义元素(对象)。

(二)创建组件的两种方式

官方推荐组件标签名是使用-连接的组合词,例如:<my-hello></my-hello>

1、使用构造器创建组件

使用这种方式创建组件首先需要使用Vue.extend()创建一个组件构造器,然后使用Vue.component(标签名,组件构造器),根据组件构造器来创建组件。

//1.创建构造器
var MyComponent=Vue.extend({
    template:'<h3>Hello World</h3>'
});
//2.创建组件
Vue.component('my-hello',MyComponent);

//3.使用组件
<div id="app">
    <my-hello></my-hello>
</div>

这种创建组件的方式比较麻烦,使用的较少。

2、直接创建组件

使用Vue.component(标签名,组件模板),根据组件构造器来创建组件。

//1.创建组件
 Vue.component('my-world', {
    template: '<h2>hello vue.js</h2>'
 });
 
//2.使用组件
<div id="app">
    <my-world></my-world>
</div>

(三)组件的分类

组件分为全局组件和局部组件。

1、全局组件

使用Vue.component()创建的组件都是全局组件。这样的组件在任何组件内都能使用。上面我们创建就是全局组件。

2、局部组件

局部组件一般都是定义在实例的选项中,称为实例的子组件。相应的,实例也被称为父组件。

//1.定义组件
 new Vue({
    el: '#app',
    components: {
        dawei: {
            template: '<h2>my name is dawei</h2>'
        }
    }
});
//2.使用组件
<div id="app">
    <dawei></dawei>
</div>

(四)引用模板

很多时候我们的template模板中需要存放很多标签内容,这样的话写起来会很麻烦。这时候我们可以使用template标签。

用法如下:

<template id="wbs">    //使用template标签
    <div>
        <h2>hello {{msg}}</h2>
        <ul>
            <li v-for="value in arr">
                {{value}}
            </li>
        </ul>
    </div>
</template>
    
    new Vue({
        el: '#app',
        components: {
            'my-dawei': {
                template: '#wbs',  //选择template标签
                data() {
                    return {
                        msg: 'vue.js',
                        arr: ["a", "b", "c", "d"]
                    }
                }
            }
        }
    });

这里涉及到的几个知识点得着重提一下:

  • template模板中,所有的元素必须放置在一个根元素中,要不然会报错。例子中我们将元素放置在了<div>标签中。
  • 组件中的data选项必须是一个函数类型,使用return返回所有的数据。

(五)动态组件

很多时候项目中需要在某一个地方动态的使用不同的组件,这时候就需要使用动态组件。
动态组件的使用需要绑定is属性:

<component :is="flag"></component>

简单示例:

//点击按钮显示不同的组件
 <div id="app">
    <button type="button" @click="flag='my-a'">显示a组件</button>
    <button type="button" @click="flag='my-b'">显示b组件</button>

    <component :is="flag"></component>  //传入flag
</div>

new Vue({
    el: '#app',
    data: {
        flag: 'my-a'   //给flag赋值
    },
    components: {
        'my-a': {
            template: '<p>我是a组件</p>',
        },
        'my-b': {
            template: '<p>我是b组件</p>'
        }
    }
});

(六)keep-alive组件

使用keep-alive组件缓存非活动组件,可以保留状态,避免重新渲染,默认每次都会销毁非活动组件并重新创建。

使用范例:

<keep-alive>
     <component :is="flag"></component>
</keep-alive>

 <div id="app">
    <button type="button" @click="flag='my-x'">x</button>
    <button type="button" @click="flag='my-y'">y</button>
    <keep-alive>
        <component :is="flag"></component>
    </keep-alive>
 </div>
 
 new Vue({
    el: '#app',
    data: {
        flag: 'my-x'
    },
    components: {
        'my-x': {
            template: '<p>{{x}}</p>',
            data() {
                return {
                    x: Math.random()
                }
            }
        },
        'my-y': {
            template: '<p>{{y}}</p>',
            data() {
                return {
                    y: Math.random()
                }
            }
        }
    }
 });

这样的话,第一次产生的随机数就会被缓存,再次切换的时候也不会发生改变。

二、 组件间数据传递

(一)父子组件

在一个组件内部定义另一个组件,那么这对组件称为父子组件。子组件只能在父组件内部使用。默认情况下,每个组件实例的作用域是独立的,子组件无法访问父组件中的数据,同样,父组件也无法访问子组件中的数据。


<div id="app">
    <my-a></my-a>
        <!-- 父组件 -->
</div>

<template id="a">
    <div>
        <p>{{msg}}</p> 
        <my-b></my-b>    <!-- 在父组件中调用子组件 -->
    </div>
</template>

<template id="b">
    <div>
        <p>{{mydata}}</p>
    </div>
</template>

<script>
    new Vue({   //根组件
        el: '#app',
        components: {     //子组件写在components选项中
            "my-a": {     //b组件的父组件
                template: "#a",
                data() {
                    return {
                        msg: '我是父组件',
                    }
                },
                components: { //子组件写在父组件的components选项中
                    "my-b": {
                        template: "#b",
                        data() {
                            return {
                                mydata: "我是子组件"
                            }
                        }
                    }
                }
            }
        }
    });
</script>

(二)组件间数据传递(通信)

1、子组件访问父组件的数据

步骤:

  • a、调用子组件时,绑定想要获取的父组件中的数据
  • b、在子组件内部,使用props选项声明获取的数据,即接收来自父组件的数据

改进上面的例子:

<div id="app">
    <my-a></my-a>
</div>

<template id="a">
    <div>
        <p>{{msg}}</p> 
        <p>这是要传递给子组件的值:{{myname}}</p>
        <my-b :name="myname"></my-b>   <!-- 绑定子组件想要获取的数据 -->
    </div>
</template>

<template id="b">
    <div>
        <p>{{mydata}}</p>
        <p>这是父组件传递过来的数据:{{name}}</p>
    </div>
</template>

<script>
    new Vue({
        el: '#app',
        data: {},
        components: {
            "my-a": {
                template: "#a",
                data() {
                    return {
                        msg: '我是a组件',
                        myname: '子组件b你好,我是父组件a'
                    }
                },
                components: {
                    "my-b": {
                        template: "#b",
                        data() {
                            return {
                                mydata: "我是b组件"
                            }
                        },
                        props: ["name"] //子组件使用props声明想要获取的数据
                    }
                }
            }
        }
    });
</script>

2、父组件访问子组件的数据

步骤:

  • a 在子组件中使用vm.$emit(事件名,数据)触发一个自定义事件,将数据发送给父组件,事件名自定义
  • b 父组件在使用子组件的地方监听子组件触发的事件,并在父组件中定义方法,用来获取数据
//子组件‘my-b’内部
methods:{
    send(){//使用$emit()触发一个事件,发送数据,this指当前子组件实例
        this.$emit('e-world', this.senddata);         
    }
}

//在调用子组件的地方监听子组件触发的事件,调用自己的方法获取数据
<my-b @e-world="getData"></my-b>   


 methods: {
    getData(data) {  //参数是子组件传递过来的数据
        this.revicedata = data;
    }
 }

3、单向数据流

props是单向数据绑定的,当父组件数据发生变化时,将传递给子组件,但是不会反过来。而且不允许子组件直接修改父组件中的数据,强制修改会报错。

解决方案:

  • 如果子组件想把它作为局部数据来使用,可以将数据存入另一个变量中再操作,不影响父组件中的数据
  • 如果子组件想修改数据并且同步更新到父组件,两个方法:
    • 使用.sync显式地触发一个更新事件(1.0版本中支持,2.0版本中不支持,2.3版本又开始支持)
//使用.sync
 <my-b :name.sync="myname"></my-b> 
 
//子组件修改父组件传入的值name,触发update更新事件
this.$emit('update:name', "vuejs"); 
    • 可以将父组件中的数据包装成对象,然后在子组件中修改对象的属性(因为对象是引用类型,指向同一个内存空间),推荐使用这种方式。
 data() {
    return { //将要传递的数据放入message对象中
        message: {
            hello: '子组件b你好,我是父组件a'
        }
    }
}

<my-b :message="message"></my-b>   //传递这个对象给子组件

 methods: {   //在子组件内部修改这个值,这样就会同步传递给父组件。
    edit() {
        this.message.hello = "hahahahh";
    }
 }

4. 非父子组件间的通信

非父子组件间的通信,可以通过一个空的Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现非父子组件间的通信。

var Event = new Vue();    //空vue实例

 methods: {  
    send() {  //触发emit事件
        Event.$emit("hello", this.asmsg);
    }
 }
 
 mounted() {    //在子组件的钩子函数中监听该事件
    Event.$on('hello', data => {   //获取值
        this.bsmsg = data;  
    })
 }

三、slot内容分发

用来获取组件中的原内容

var vm = new Vue({
    el: '#app',
    components: {
        'my-hello': {
            template: '#hello'
        }
    }
});

<div id="app">
    <my-hello>hello vue.js</my-hello>
</div>


<template id="hello">
    <div>
        <slot>如果没有原内容,则显示该内容</slot>
    </div>
</template>

如果组件标签中没有内容就会显示slot中的内容,这也就是所谓的单个插槽。

还可以对显示的内容进行分组,这就是具名插槽,可以操作标签组中的内容:

<div id="app">
    <my-hello>
        <ul slot="s1">
            <li>aaa</li>
            <li>bbb</li>
            <li>ccc</li>
        </ul>
        <ol slot="s2">
            <li>111</li>
            <li>222</li>
            <li>333</li>
        </ol>
    </my-hello>
</div>

<template id="hello">
    <div>
        <slot name="s2"></slot> //为插槽指定名称  将名为s2的内容放置在这里
        <p>hello vue.js</p>
        <slot name="s1"></slot>  //将名为s1的内容放置在这里
    </div>
</template>

这样,就可以对组件中的内容实时操作。


间阳幕宾
309 声望47 粉丝

在校大学生,一位爱生活、爱分享的准程序员。